class DotProducer
  @__nesting__ = 0
  attr_reader :__text__
  
  def initialize __nesting__ = 0
    @__nesting__ = __nesting__
    @__text__ = ""
  end
  
  def __indent__
    "  " * @__nesting__
  end
  
  def method_missing symbol, *args
    options = Hash.new
    args.each.with_index do |arg,index|
      if arg.respond_to? :merge
        options.merge!(arg)
        args.delete_at index
      else
        arg = arg.to_s
      end
    end
    @__text__ << __indent__ + "#{symbol}"
    @__text__ << " #{args.join(" ")}" if args.length > 0
    kvtext = options.map do |k,v|
      k.to_s + "=\"" + v.to_s + "\""
    end.join(" ")
    unless options.length == 0
      @__text__ << "[#{kvtext}]"
    end
    
    if block_given?
      @__text__ << " {\n"
      tmp = @__nesting__
      @__nesting__ = @__nesting__.succ
      @__text__ << (yield DotProducer.new @__nesting__).__text__
      @__nesting__ = tmp
      @__text__ << __indent__ + "}"
    end
    
    @__text__ << (block_given? ? "\n" : ";\n")
    
    self
  end
  
  def command symbol, options=Hash.new
    method_missing symbol, options
  end
  
  def connect head, tail, options=Hash.new
    if tail.respond_to? :each
      
      tail.each do |item|
        connect head, item, options
      end
    else 
      method_missing "#{head}->#{tail}", options
    end
    
    self
  end
  
  def settings options=Hash.new
    options.each do |key, value|
      method_missing "#{key.to_s}=\"#{value.to_s}\""
    end
    
    self
  end
  
  alias_method :__cmd__, :method_missing
  alias_method :__env__, :method_missing
end

class Dot
  attr_reader :code
  
  def initialize *args
    @code = (yield DotProducer.new).__text__
  end
  
  def compile *args, options
    params_string = args.map{|i| i[0] == "-" ? i : "-#{i}"}.join(" ") + " " + \
                    options.map{ |k,v| 
                      k = k[0] == "-" ? k : "-#{k}"
                      if /(?:G|N|E)name/.match(k).nil?
                        "#{k}#{v}"
                      else
                        "#{k}=\"#{v}\""
                      end
                    }.join(" ")
    tmp_file = File.new "graph_#{Time.now.to_i}", "w+"
    tmp_file.puts @code
    tmp_file.close
    %x"dot #{params_string} #{tmp_file.path}"
    File.delete tmp_file.path
    self
  end
  
  def export filename
    file = File.new filename.to_s, "w+"
    file.puts @code
    file.close
    file.path
  end
  
  alias_method :to_f, :export
  alias_method :to_file, :export
  alias_method :data, :code
end

if __FILE__ == $0
  Dot.new { |dot|
    dot.digraph(:test) do |dig|
      dig.node(:shape=>:circle)
      dig.b(:shape=>:square)
      dig.connect(:a, :b, :label=>"Testing...")
    end
    
    dot.digraph(:test2) do |dig|
      dig.node(:shape=>:circle)
      dig.b(:shape=>:doublecircle)
      dig.connect(:testing, :b, :label=>"LABELS ARE AWESOME...")
    end
  }.compile :O, :T =>:svg
end